from typing import List, Dict, Any
from urllib.parse import urljoin

import httpx

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_current_user, get_current_active_superuser
from app.core.database import get_db
from app.core.config import settings
from app.core.logging_config import get_logger
from app.crud.user import user as user_crud
from app.crud.patient_profile import patient_profile as patient_profile_crud
from app.crud.doctor_profile import doctor_profile as doctor_profile_crud
from app.models.user import User, UserType
from app.schemas.base import UnifiedResponse, success_response
from app.schemas.user import (
    User as UserSchema,
    UserCreate,
    UserUpdate,
    PasswordChange,
    PatientProfile as PatientProfileSchema,
    PatientProfileCreate,
    PatientProfileUpdate,
    DoctorProfile as DoctorProfileSchema,
    UserProfileResponse,
)
from app.utils.exceptions import (
    UserAlreadyExistsException,
    UserNotFoundException,
)

router = APIRouter()
logger = get_logger(__name__)


async def _request_certificate_creation(document_number: str) -> Dict[str, Any]:
    """Call external service to create a certificate for the given document number."""
    if not settings.CERTIFICATE_BASE_URL:
        raise HTTPException(status_code=500, detail="证书服务未配置")

    create_path = settings.CERTIFICATE_CREATE_PATH or ""
    create_url = urljoin(
        settings.CERTIFICATE_BASE_URL.rstrip("/") + "/",
        create_path.lstrip("/"),
    )

    payload = {
        "algorithm": {"type": "Ed25519"},
        "username": document_number,
    }
    headers = {
        "accept": "application/json",
        "Content-Type": "application/json",
    }

    try:
        async with httpx.AsyncClient(timeout=settings.CERTIFICATE_REQUEST_TIMEOUT) as client:
            response = await client.post(
                create_url,
                json=payload,
                headers=headers,
            )
    except httpx.RequestError as exc:
        logger.error("Certificate service request failed: %s", exc)
        raise HTTPException(status_code=502, detail="证书服务请求失败，请稍后再试")

    if not (200 <= response.status_code < 300):
        message: Any
        try:
            error_data = response.json()
            message = error_data.get("message") or error_data.get("detail") or error_data
        except ValueError:
            message = response.text or "证书服务返回错误"
        logger.warning(
            "Certificate service responded with status %s: %s", response.status_code, message
        )
        raise HTTPException(status_code=502, detail=message)

    try:
        return response.json()
    except ValueError as exc:
        logger.error("Invalid JSON from certificate service: %s", exc)
        raise HTTPException(status_code=502, detail="证书服务返回无效数据")


async def _fetch_user_certificates(document_number: str) -> Dict[str, Any]:
    """Retrieve certificate information for the given document number."""
    if not settings.CERTIFICATE_BASE_URL or not settings.CERTIFICATE_QUERY_PATH_TEMPLATE:
        raise HTTPException(status_code=500, detail="证书查询服务未配置")

    query_path = settings.CERTIFICATE_QUERY_PATH_TEMPLATE.format(document_number=document_number)
    url = urljoin(
        settings.CERTIFICATE_BASE_URL.rstrip("/") + "/",
        query_path.lstrip("/"),
    )
    headers = {
        "accept": "application/json",
    }

    try:
        async with httpx.AsyncClient(timeout=settings.CERTIFICATE_REQUEST_TIMEOUT) as client:
            response = await client.get(url, headers=headers)
    except httpx.RequestError as exc:
        logger.error("Certificate query request failed: %s", exc)
        raise HTTPException(status_code=502, detail="证书查询失败，请稍后再试")

    if response.status_code != 200:
        message: Any
        try:
            error_data = response.json()
            message = error_data.get("message") or error_data.get("detail") or error_data
        except ValueError:
            message = response.text or "证书查询服务返回错误"
        logger.warning(
            "Certificate query responded with status %s: %s", response.status_code, message
        )
        raise HTTPException(status_code=502, detail=message)

    try:
        return response.json()
    except ValueError as exc:
        logger.error("Invalid JSON from certificate query service: %s", exc)
        raise HTTPException(status_code=502, detail="证书查询服务返回无效数据")


async def _check_unique_fields(db: AsyncSession, user_in: UserCreate) -> None:
    if await user_crud.get_by_phone(db, phone=user_in.phone):
        raise UserAlreadyExistsException("phone")

    if user_in.document_number:
        existing = await user_crud.get_by_document_number(
            db,
            document_number=user_in.document_number,
        )
        if existing:
            raise UserAlreadyExistsException("document_number")

    if user_in.email:
        existing = await user_crud.get_by_email(db, email=user_in.email)
        if existing:
            raise UserAlreadyExistsException("email")

    if user_in.username:
        existing = await user_crud.get_by_username(db, username=user_in.username)
        if existing:
            raise UserAlreadyExistsException("username")


@router.post("/", response_model=UnifiedResponse[UserSchema])
async def create_user(
    *,
    db: AsyncSession = Depends(get_db),
    user_in: UserCreate,
    current_user: User = Depends(get_current_active_superuser),
) -> UnifiedResponse[UserSchema]:
    """
    Create new user (superuser only).
    """
    await _check_unique_fields(db, user_in)

    user = await user_crud.create(db, obj_in=user_in)
    return success_response(UserSchema.model_validate(user))


@router.get("/", response_model=UnifiedResponse[List[UserSchema]])
async def read_users(
    db: AsyncSession = Depends(get_db),
    skip: int = 0,
    limit: int = 100,
    current_user: User = Depends(get_current_active_superuser),
) -> UnifiedResponse[List[UserSchema]]:
    """
    Retrieve users (superuser only).
    """
    users = await user_crud.get_multi(db, skip=skip, limit=limit)
    data = [UserSchema.model_validate(item) for item in users]
    return success_response(data)


@router.get("/{user_id}", response_model=UserSchema)
async def read_user(
    *,
    db: AsyncSession = Depends(get_db),
    user_id: str,
    current_user: User = Depends(get_current_user),
) -> UserSchema:
    """
    Get user by ID.
    """
    if user_id != str(current_user.id) and not user_crud.is_superuser(current_user):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions",
        )

    user = await user_crud.get(db, id=user_id)
    if not user:
        raise UserNotFoundException()

    return UserSchema.model_validate(user)


@router.put("/{user_id}", response_model=UnifiedResponse[UserSchema])
async def update_user(
    *,
    db: AsyncSession = Depends(get_db),
    user_id: str,
    user_in: UserUpdate,
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[UserSchema]:
    """
    Update user profile (self or superuser).
    """
    if user_id != str(current_user.id) and not user_crud.is_superuser(current_user):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions",
        )

    user = await user_crud.get(db, id=user_id)
    if not user:
        raise UserNotFoundException()

    if user_in.phone and user_in.phone != user.phone:
        existing = await user_crud.get_by_phone(db, phone=user_in.phone)
        if existing:
            raise UserAlreadyExistsException("phone")

    if user_in.document_number and user_in.document_number != user.document_number:
        existing = await user_crud.get_by_document_number(
            db,
            document_number=user_in.document_number,
        )
        if existing:
            raise UserAlreadyExistsException("document_number")

    if user_in.email and user_in.email != user.email:
        existing = await user_crud.get_by_email(db, email=user_in.email)
        if existing:
            raise UserAlreadyExistsException("email")

    if user_in.username and user_in.username != user.username:
        existing = await user_crud.get_by_username(db, username=user_in.username)
        if existing:
            raise UserAlreadyExistsException("username")

    updated_user = await user_crud.update(db, db_obj=user, obj_in=user_in)
    return success_response(UserSchema.model_validate(updated_user))


@router.delete("/{user_id}", response_model=UnifiedResponse[UserSchema])
async def delete_user(
    *,
    db: AsyncSession = Depends(get_db),
    user_id: str,
    current_user: User = Depends(get_current_active_superuser),
) -> UnifiedResponse[UserSchema]:
    """
    Delete user (superuser only).
    """
    user = await user_crud.get(db, id=user_id)
    if not user:
        raise UserNotFoundException()

    removed = await user_crud.remove(db, id=user_id)
    return success_response(UserSchema.model_validate(removed), msg="User deleted successfully")


@router.put("/me/profile", response_model=UnifiedResponse[UserSchema])
async def update_user_profile(
    *,
    db: AsyncSession = Depends(get_db),
    profile_in: UserUpdate,
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[UserSchema]:
    """
    Update current user's profile information.
    """
    profile_data = profile_in.model_dump(exclude_unset=True)
    for field in {"is_superuser", "is_active", "verification_status", "user_type"}:
        profile_data.pop(field, None)

    safe_update = UserUpdate(**profile_data)
    updated_user = await user_crud.update(db, db_obj=current_user, obj_in=safe_update)
    return success_response(UserSchema.model_validate(updated_user), msg="Profile updated successfully")


@router.get("/me/profile", response_model=UnifiedResponse[UserProfileResponse])
async def get_user_profile(
    *,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[UserProfileResponse]:
    """
    Get current user's profile information with role-specific details.
    """
    patient_profile_data = None
    doctor_profile_data = None

    if current_user.user_type == UserType.PATIENT:
        profile = await patient_profile_crud.get_by_user_id(db, user_id=str(current_user.id))
        if profile:
            patient_profile_data = PatientProfileSchema.model_validate(profile)
    elif current_user.user_type == UserType.DOCTOR:
        profile = await doctor_profile_crud.get_by_user_id(db, user_id=str(current_user.id))
        if profile:
            doctor_profile_data = DoctorProfileSchema.model_validate(profile)

    payload = UserProfileResponse(
        user=UserSchema.model_validate(current_user),
        patient_profile=patient_profile_data,
        doctor_profile=doctor_profile_data,
    )
    return success_response(payload)


@router.post("/me/certificate", response_model=UnifiedResponse[Dict[str, Any]])
async def apply_user_certificate(
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[Dict[str, Any]]:
    """Request a certificate for the current user using their document number."""

    document_number = (current_user.document_number or "").strip()
    if not document_number or document_number.lower() in {"null", "none"}:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="当前用户未填写证件号",
        )

    certificate_data = await _request_certificate_creation(document_number)
    return success_response(certificate_data, msg="证书申请成功")


@router.get("/me/certificate", response_model=UnifiedResponse[Dict[str, Any]])
async def get_user_certificate(
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[Dict[str, Any]]:
    """Retrieve certificate information for the current user."""

    document_number = (current_user.document_number or "").strip()
    if not document_number or document_number.lower() in {"null", "none"}:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="当前用户未填写证件号",
        )

    certificate_data = await _fetch_user_certificates(document_number)
    return success_response(certificate_data, msg="证书查询成功")


@router.put("/me/patient-profile", response_model=UnifiedResponse[PatientProfileSchema])
async def update_patient_profile(
    *,
    db: AsyncSession = Depends(get_db),
    payload: PatientProfileUpdate,
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[PatientProfileSchema]:
    """Update current patient's height/weight information."""

    if current_user.user_type != UserType.PATIENT:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="仅患者可以修改该信息",
        )

    if payload.height_cm is None and payload.weight_kg is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="身高或体重至少填写一项",
        )

    profile = await patient_profile_crud.get_by_user_id(db, user_id=str(current_user.id))

    latest_height = payload.height_cm if payload.height_cm is not None else (
        profile.height_cm if profile else None
    )
    latest_weight = payload.weight_kg if payload.weight_kg is not None else (
        profile.weight_kg if profile else None
    )

    bmi = None
    if latest_height and latest_weight and latest_height > 0:
        bmi = round(latest_weight / ((latest_height / 100) ** 2), 1)

    if not profile:
        profile_payload = PatientProfileCreate(
            user_id=current_user.id,
            height_cm=payload.height_cm,
            weight_kg=payload.weight_kg,
            bmi=bmi,
        )
        updated_profile = await patient_profile_crud.create(db, obj_in=profile_payload)
    else:
        update_data = {}
        if payload.height_cm is not None:
            update_data["height_cm"] = payload.height_cm
        if payload.weight_kg is not None:
            update_data["weight_kg"] = payload.weight_kg
        if bmi is not None:
            update_data["bmi"] = bmi
        updated_profile = await patient_profile_crud.update(db, db_obj=profile, obj_in=update_data)

    return success_response(
        PatientProfileSchema.model_validate(updated_profile),
        msg="个人健康信息已更新",
    )


@router.put("/me/password", response_model=UnifiedResponse[dict])
async def change_password(
    *,
    db: AsyncSession = Depends(get_db),
    password_change: PasswordChange,
    current_user: User = Depends(get_current_user),
) -> UnifiedResponse[dict]:
    """
    Change current user's password.
    """
    if not user_crud.authenticate(current_user, password_change.old_password):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="原密码不正确",
        )

    if password_change.old_password == password_change.new_password:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="新密码不能与原密码相同",
        )

    await user_crud.update(
        db,
        db_obj=current_user,
        obj_in=UserUpdate(password=password_change.new_password),
    )

    return success_response({"message": "密码修改成功"}, msg="密码修改成功")
